#include "CPESBuilder.h"
#include <algorithm>

using namespace std;

CPESBuilder::CPESBuilder(std::set<unsigned int> pids, EFixMode eMode): m_pids(pids), m_eMode(eMode)
{
	set<unsigned int>::iterator it;
	for (it = m_pids.begin(); it != m_pids.end(); it++){
		unsigned int PID = *it;
		m_cLastPes[PID] = NULL;
		m_nLastPesCounter[PID] = 0;
		m_lPesList[PID] = new std::vector<CPES*>();
	}
}
/** 
d'tor. 
deletes the PES lists  and the list that keeps pointers to unfinished PESs
*/
CPESBuilder::~CPESBuilder(void)
{
	map<unsigned int, CDynamicArray*>::iterator iter1;
	for (iter1=m_cLastPes.begin(); iter1 != m_cLastPes.end(); ++iter1) {
		CDynamicArray* arr = iter1->second;		
		delete arr;
	}

	map<unsigned int, std::vector<CPES*>*>::iterator iter2;
	for (iter2=m_lPesList.begin(); iter2 != m_lPesList.end(); ++iter2) {
		std::vector<CPES*>* vec = iter2->second;
		for(unsigned int j=0; j<vec->size(); j++)
			delete vec->at(j);
		delete vec;
	}	
}


void CPESBuilder::_addTSToPes(unsigned int PID,CTS* CTSpacket){
	m_nLastPesCounter[PID] = CTSpacket->getContinuityCounter();
	CDynamicArray* cLastPes = m_cLastPes[PID];
    // get the TS header length,according to the TS's AdaptationFieldControl
	unsigned int nHeaderLength=CTSpacket->getHeaderLength();  
	
	
	if (CTSpacket->getAdaptationFieldControl()==1 || CTSpacket->getAdaptationFieldControl()==3)
	{
		// copies the TS payload to the unfinished PES array 
		cLastPes->copyToArray((CTSpacket->getData())+nHeaderLength, 188 - nHeaderLength); 
	}
	return;
}

void CPESBuilder::buildPes(ITSBuilder* cTSBuilder)
{
	// iterates over all the TS lists,each iteration of this for is another TS list 
	set<unsigned int>::iterator it;
	for (it = m_pids.begin(); it != m_pids.end(); it++){
		unsigned int PID = *it;
		std::vector<CTS*>* vec = cTSBuilder->getTs(PID);
		if (NULL == vec)
			continue;
		CTS* sCurrentTS;
        // for iterates over the same TS list (all the TSs in the list have the same PID)
		for(unsigned int i=0; i<vec->size(); i++)   
		{
			sCurrentTS = vec->at(i);
            // if the TS begins a new PES
			if (sCurrentTS->getPayloadStartIndicator() == 1)   
			{
				// if there is un unfinished PES from the previous time the data was read from the stream file
				if (NULL != m_cLastPes[PID]) 
				{
					//creates a new PES with the PID of the current TS 
					CPES* cPes = new CPES(m_cLastPes[PID]); 
                    //puts the finishished TS to the relevant PES list 
					m_lPesList[PID]->push_back(cPes); 
					
					std::sort(m_lPesList[PID]->begin(), m_lPesList[PID]->end(), PesSortFunction);
                    //deletes the just finished PES from the unfinished PESs list 
					delete m_cLastPes[PID]; 
				}
                // creates a new unfinished PES 
				m_cLastPes[PID] = new CDynamicArray(); 
				// copies the data from the current TS to the unfinished PES 
				_addTSToPes(PID, sCurrentTS); 
			}
            // if the TS doesn't begin a new PES and there is an unfinished PES
			else if (m_cLastPes[PID] != NULL) 
			{
				// if the current TS's continuity counter equals the last TS continuity counter+1 
				bool bAddTs = (sCurrentTS->isNextTo(m_nLastPesCounter[PID]));
				switch (m_eMode){
					case none:
						break;
					case allowAny:
						bAddTs = true;
						break;
					case allowFew:
						bAddTs = bAddTs || (sCurrentTS->isNextTo((m_nLastPesCounter[PID]+1)%15));
						//bAddTs = bAddTs || (sCurrentTS->isNextTo((m_nLastPesCounter[PID]+2)%15));
						break;
					case considerLength:
						CDynamicArray* cLastPes = m_cLastPes[PID];
						if (cLastPes->m_nSize>=6){
							unsigned int PES_packet_length = (cLastPes->m_cArray[4]& 0xff)*(256) + (cLastPes->m_cArray[5]& 0xff);
							bAddTs = bAddTs || PES_packet_length > cLastPes->m_nSize;
						}
						break;
				}
				if (bAddTs)
				{
					//copies the data from the current TS to the unfinished PES
					_addTSToPes(PID,sCurrentTS);   
				}
			}

			delete sCurrentTS;
		}
		vec->clear();
	}	
}

std::vector<CPES*>* CPESBuilder::getPESPackets(unsigned int PID){
	if (m_lPesList.find(PID) == m_lPesList.end())
		return NULL;
	return m_lPesList[PID];
}
